import logging
import pickle
import re

import requests
from requests import Response
from requests.cookies import RequestsCookieJar
from utils.myrsa import Encrypt
from plugins_v3._login import config
from utils.exceptions import custom_abort
from utils.redis_connections import redis_session, redis_experiment
from utils.session import session


# 主体账号登录
def login(name: str, passwd: str, disable_cache=False, all_cookies=True) -> RequestsCookieJar:
    if not name or not passwd:
        custom_abort(-3, '账号密码不能为空!')
    if name.strip() != name:
        custom_abort(-3, '用户名包含空字符!')
    # 如果存在缓存
    if not disable_cache:
        cookie_pickle = redis_session.get("cookie" + name)
        if cookie_pickle:
            cookies: RequestsCookieJar = pickle.loads(cookie_pickle)
            r = session.get(config.login_test_url, allow_redirects=False, cookies=cookies)
            # 如果保存的cookie可以正常使用的话
            if r.status_code == 200 and r.content.decode().find("自定义门户") != -1:
                cookies.update(r.cookies)
                if all_cookies:
                    if ".222.31.49.139" in cookies.keys():
                        cookies.clear(".222.31.49.139")
                    res, cookies = follow_link(cookies, config.jwxt_url)
                    # 如果保存的cookie可以正常进入教务系统
                    if res.status_code == 200 and res.content.decode().find("教学管理信息服务平台") != -1:
                        redis_session.set("cookie" + name, pickle.dumps(cookies), ex=21600)
                        # 命中缓存
                        return cookies
                    else:
                        redis_session.delete("cookie" + name)
                        logging.info("cookies" + name + "过期删除, code为:" + str(res.status_code))
    # 未命中缓存,进行登录,如果错误就重试
    login_response, cookies = re_login(re_url=config.index_url, e_url=config.e_login_url, name=name, passwd=passwd)
    # 正式迭代登录并判断登录结果
    cookies = check_login(cookies=cookies, login_response=login_response)
    if all_cookies:
        # 获取教务系统cookie
        _, cookies = follow_link(cookies, config.jwxt_url)
    redis_session.set("cookie" + name, pickle.dumps(cookies), ex=21600)
    return cookies


# 实验系统登录
def experiment_login(name: str, passwd: str) -> RequestsCookieJar:
    cookie_pickle = redis_experiment.get("experiment" + name)
    # 如果命中缓存
    if cookie_pickle:
        cookies: RequestsCookieJar = pickle.loads(cookie_pickle)
        r = session.get(config.experiment_test_url, allow_redirects=False, cookies=cookies)
        # 如果缓存可以正常使用
        if r.status_code == 200 and r.content.decode().find("中北大学实践教学管理平台") != -1:
            cookies.update(r.cookies)
            redis_experiment.set("experiment" + name, pickle.dumps(cookies), ex=21600)
            return cookies
        else:
            redis_experiment.delete("experiment" + name)
            logging.info("experiment" + name + "过期删除, code为:" + str(r.status_code))

    # 未命中缓存,进行登录,如果错误就重试
    login_response, cookies = re_login(re_url=config.experiment_url, e_url=config.e_experiment_url, name=name, passwd=passwd)
    # 正式迭代登录并判断登录结果
    cookies = check_login(cookies=cookies, login_response=login_response)
    redis_experiment.set("experiment" + name, pickle.dumps(cookies), ex=21600)
    return cookies


# 体育管理系统登录
def physical_login(name: str, passwd: str) -> RequestsCookieJar:
    login_response, cookies = re_login(re_url=config.physical_url, e_url=config.e_physical_url, name=name, passwd=passwd)
    # 正式迭代登录并判断登录结果
    cookies = check_login(cookies=cookies, login_response=login_response)
    return cookies


# 登录以及重新登录
def re_login(re_url, e_url, name, passwd):
    re_num = 0
    while True:
        index_response = session.get(re_url)
        if index_response.content.decode().find("Loading...") != -1:
            custom_abort(-1, 'VPN通道已关闭!')
        # 获取用于提交登录的数据和cookie
        cookies, post_data = ready_login(index_response=index_response, name=name, passwd=passwd)
        # 提交登录
        login_response = session.post(re_url, allow_redirects=False, data=post_data, cookies=cookies)
        if login_response.headers.get('Location') != e_url or re_num > 3:
            break
        re_num += 1
        logging.warning('{}登录异常,第{}次重新发送登录请求!'.format(name, re_num))
    return login_response, cookies


# 跟随一系列 302 跳转，并更新 cookies
def follow_link(cookies: RequestsCookieJar, url: str) -> (Response, RequestsCookieJar):
    while True:
        resp = session.get(url, cookies=cookies, allow_redirects=False)
        cookies.update(resp.cookies)
        # session中已经拒绝了https,所以这里是http
        if not resp.headers.get('location') or resp.headers.get('location').find('http') == -1:
            return resp, cookies
        else:
            url = resp.headers.get('location')


# 请求的数据
def ready_login(index_response: Response, name: str, passwd: str):
    cookies = index_response.cookies
    execution = re.search('name="execution" value="(.*?)"', index_response.content.decode()).group(1)
    public_key_dict_resp = session.get(config.public_key_url, cookies=cookies)
    cookies.update(public_key_dict_resp.cookies)
    public_key_dict = public_key_dict_resp.json()
    post_data = {
        'authcode': '',
        '_eventId': 'submit',
        'execution': execution,
        'username': name,
        # 获取rsa加密的密码
        'password': Encrypt(public_key_dict["exponent"], public_key_dict["modulus"]).encrypt(passwd[::-1])
    }
    return cookies, post_data


# 判断登录结果,更新cookies并返回
def check_login(cookies: RequestsCookieJar, login_response: Response):
    if login_response.status_code == 302 and config.up_url in login_response.headers.get('location'):
        custom_abort(-3, '请修改密码!')
    cookies.update(login_response.cookies)
    if login_response.status_code == 302:
        login_response, cookies = follow_link(login_response.cookies, login_response.headers.get('location'))
    login_response_html = login_response.content.decode()
    if login_response_html.find("用户名或密码错误") != -1:
        custom_abort(-3, '账号或密码错误!')
    elif login_response_html.find("完成，进入门户") != -1:
        custom_abort(-3, '未绑定手机号!')
    elif login_response_html.find("当前账号无权登录") != -1:
        custom_abort(-3, '无权登录此账号!')
    elif login_response_html.find("统一身份认证") != -1:
        custom_abort(-3, '未知登录错误!')
    return cookies
